package top.ffhome.common.audit;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import top.ffhome.common.entity.IdEntity;

/**
 * 业务数据变更日志记录AOP抽象超类,如何记录日志（存数据库或文件）由子类实现<br/>
 * Spring配置文件中增加<aop:aspectj-autoproxy proxy-target-class="true" />或增加@EnableAspectJAutoProxy<br/>
 * 在Service层类中增加@AuditService(entity = Area.class),参数是实体类对象<br/>
 * 相应的操作方法增加@AuditOperation(type = AuditOperation.INSERT),参数为操作类型
 * 
 * @author John
 */
public abstract class AbstractAuditLogAspect {
	@Autowired
	protected ObjectMapper mapper;

	@Pointcut("@annotation(top.ffhome.common.audit.AuditOperation)")
	public void auditOperationAspect() {
	}

	/**
	 * 被拦截方法执行返回后执行此方法
	 * 
	 * @param joinPoint
	 * @param rvt
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@AfterReturning(pointcut = "auditOperationAspect()", returning = "rvt")
	public void doAfterReturning(JoinPoint joinPoint, Object rvt) {
		Class clazz = joinPoint.getTarget().getClass();
		if (clazz.isAnnotationPresent(AuditService.class)) {
			AuditService classA = (AuditService) clazz.getAnnotation(AuditService.class);
			String className = clazz.getName();
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			Method method = signature.getMethod();
			String methodName = signature.getName();
			String[] argNames = signature.getParameterNames();
			Object[] argValues = joinPoint.getArgs();

			if (method != null) {
				AuditOperation methodA = (AuditOperation) method.getAnnotation(AuditOperation.class);
				String functionPath = className + "." + methodName;
				writeAuditLog(functionPath, methodA.type(), classA.entity().getSimpleName(),
						getDataId(classA.entity(), argNames, argValues), getContent(argValues));
			}
		}
	}

	private Long getDataId(Class<?> entityClass, String[] argNames, Object[] argValues) {
		for (int i = 0; i < argNames.length; i++) {
			if ("id".equals(argNames[i])) {
				return (Long) argValues[i];
			}
		}
		for (Object argValue : argValues) {
			if (argValue != null && entityClass == argValue.getClass()) {
				return ((IdEntity) argValue).getId();
			}
		}

		for (Object argValue : argValues) {
			if (argValue != null && argValue instanceof IdEntity) {
				return ((IdEntity) argValue).getId();
			}
		}
		return null;
	}

	private String getContent(Object[] argValues) {
		try {
			return mapper.writeValueAsString(argValues);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		return "";
	}

	/**
	 * 由子类实现的记录日志方法
	 * 
	 * @param functionPath
	 * @param type
	 * @param entityName
	 * @param dataId
	 * @param content
	 */
	protected abstract void writeAuditLog(String functionPath, int type, String entityName, Long dataId,
			String content);
}
